home *** CD-ROM | disk | FTP | other *** search
Text File | 1996-08-15 | 43.2 KB | 1,406 lines | [TEXT/ALFA] |
- ###########################################################################
- # bibtex.tcl
- #
- # This file contains a package of Tcl routines that add support for using
- # and maintaining BibTeX citation databases to Alpha.
- #
- # See the accompanying file, "BibTeX Help", for a complete description.
- # (Unfortunately, it's a bit out of date right now - stay tuned (WTP 6/95))
- #
- ###########################################################################
- # Notes:
- #
- # By default, only the required fields are included when a new bib entry
- # is created. You can select any other set of fields to be used by adding
- # an appropriate entry to the 'myFld' array, following the example for the
- # Article entry, further below. You shouldn't change the 'rqdFld' or
- # 'optFld' arrays, since these will (some day) be used for syntax checking.
- #
- ###########################################################################
- # written by Tom Pollard (pollard@cucbs.chem.columbia.edu)
- #
- # Version History
- #
- # 2.7 (7/95) 'stdAbbrevs' modeVar added for setting predefined abbrevs
- # month names included as predefined abbrevs
- # 'alignEquals' formatting flag added.
- # 2.62 (7/95) field delimiters suppressed if field data is an abbreviation
- # unindexed .bib files are indexed automatically upon opening
- # 2.61 (7/95) fixed "SearchFields" bug.
- # 2.6 (6/95) 'zapEmptyFields' flag forces optional fields to be removed
- # when reformatting an entry.
- # 'markStrings' flag controls whether @string entries are included in
- # the marks menu.
- # 'descendingYears' flag controls whether sorts are in ascending or
- # descending chronological order.
- # Sorts all use the year as either primary or secondary sort key now.
- # 'copyCiteKey' command copies the citekey of the current entry to the
- # clipboard.
- # Cmd-double-clicking implemented to resolve abbreviations and crossrefs.
- # Fixed bug in faster getFields proc (comma-after-last-field problems)
- # Fixed minor bugs in author sorting.
- # 2.5 (6/95) Fixed bug in formatEntry, whereby '#' concatenations were lost
- # formatEntry completely ignores @string entries now
- # Entry-parsing code (getFields, getFldVal) cleaned up,
- # should also be a little bit faster now.
- # formatAllEntries now starts working from the current entry
- # 2.41 (6/95) Updates for compatibility with revised LaTeX mode
- # Automatic conversion of international characters dropped
- # (irreconcilable problems with non-US keyboards).
- # 2.4 (5/95) Fixed bugs in parsing of EndNote-created bib files
- # 2.3 (4/95) International characters converted to TeX codes (optionally).
- # 'findEntries' bug fixed (no longer returns multiple hits)
- # 2.2 (12/94) 'formatEntries' won't quote fields that contain "#".
- # 'segregateStrings' flag forces string defs to sort to the top.
- # 2.11(12/94) Bug fixes in 'formatAllEntries'.
- # 2.1 (12/94) 'countEntries' command added.
- # 'formatAllEntries' command added; it's a bit clunky, but more robust
- # than any quicker alternative I considered.
- # Cross-referenced entries now sort to the bottom in all sorts.
- # 'crossref' field now included.
- # 2.0 (9/94) 'formatEntry' and 'newEntry' line up fields better.
- # 'nextEntry' and 'prevEntry' skip @string defs
- # 'formatEntry' automtically goes to next entry afterwards.
- # 'sortByCitekey' ignores case of cite keys.
- # 'fillColumn' included as default modeVar.
- # 'getEntry' alerts user to badly delimited entries.
- # 1.9 (9/94) 'getFields' should now correctly parse any legal entry.
- # 'language' field now included.
- # Default values for new fields (eg 'language') may be defined
- # 'preferBraces' replaced by 'fieldBraces' and 'entryBraces'.
- # line-wrapping is done on reformatted entries.
- # '@string' entries preserved in sorts.
- # text before first entry and after last entry are preserved
- # by sorts.
- # 1.8 (8/94) "getEntry" now recognizes parens as entry delimiters
- # 1.7 (8/94) Bug fixes and accomodations to latex.tcl v2.2
- # Template insertion streamlined
- # Choose multiple fields at a time from list dialog
- # 1.6 (8/94) "preferBraces" allows braces or quotes to be default for
- # new or reformatted entries,
- # Menu built using $entryNames and $fieldNames,
- # 'sortByAuthors' can now sort using last author first,
- # and is a bit faster,
- # 'formatEntry' rewrites entries in canonical format,
- # More customization of canonical format allowed ('indentString')
- # Bib mode definition adapted to Alpha 5.90.
- # 1.5 (7/94) "sortByAuthors" is now robust (I think),
- # Mode of new windows now set correctly.
- # 1.4 (7/94) Added sorting by authors, but still only semi-functional,
- # Added regexp searching by field,
- # "getEntry" bugs fixed.
- # 1.2 (7/94) Bib mode definition adapted to Alpha 5.85,
- # Added bib-file marking (bibMarkFile),
- # Entry and field creation now controlled by data arrays.
- # 1.1 (6/94) Custom BibTeX icon,
- # Added simple search capability (matchingEntries).
- # 1.0 (9/93) First stable version.
- #
- ###########################################################################
- # This package was inspired by the LaTeX package (latex.tcl), written by
- #
- # Richard T. Austin <austin@eecs.umich.edu> , and (currently),
- # Tom Scavo <trscavo@syr.edu>
- #
- ###########################################################################
- ############################################################################
- # Cause latex.tcl to be loaded by calling a dummy procedure defined in that
- # file. This is necessary to get the TeX menu, and to load the 8-bit ASCII
- # to TeX conversion routines.
- #
- dummyTeX
-
- ###########################################################################
- # BibTeX Key Bindings.
- ###########################################################################
- # abbreviations: <o> = option, <z> = control, <s> = shift, <c> = command
- #
- bind 'b' <sz> selectEntry "Bib"
- bind 'n' <sz> nextEntry "Bib"
- bind 'p' <sz> prevEntry "Bib"
-
- bind 'f' <sz> searchFields "Bib"
- bind 'm' <sz> searchEntries "Bib"
- bind 'l' <sz> formatEntry "Bib"
-
- # tab stops:
- bind '¥t' nextTabStop "Bib"
- bind '¥t' <s> prevTabStop "Bib"
- bind '¥t' <z> {nthTabStop 0} "Bib"
- bind '¥t' <c> deleteTabStops "Bib"
-
- ###########################################################################
- # Data Definitions
- ###########################################################################
- ###########################################################################
- # Define the data arrays that contain the names of the required,
- # optional, and preferred fields for each entry type.
- #
- # The index names of the rqdFld() array _define_ the valid entry types
- # recognized by the program.
- #
- set rqdFld(article) {author title journal year}
- set optFld(article) {volume number pages month note}
- set myFld(article) {author title journal volume pages year note}
-
- set rqdFld(book) {author title publisher year}
- set optFld(book) {editor volume number series address edition month note}
-
- set rqdFld(booklet) {title}
- set optFld(booklet) {author howpublished address month year note}
-
- set rqdFld(conference) {author title booktitle year}
- set optFld(conference) {editor volume number series pages organization publisher address month note}
-
- set rqdFld(inBook) {author title chapter publisher year}
- set optFld(inBook) {editor pages volume number series address edition month type note}
-
- set rqdFld(inCollection) {author title booktitle publisher year}
- set optFld(inCollection) {editor volume number series type chapter pages address edition month note}
-
- set rqdFld(inProceedings) {author title booktitle year}
- set optFld(inProceedings) {editor volume number series pages organization publisher address month note}
-
- set rqdFld(manual) {title}
- set optFld(manual) {author organization address edition year month note}
-
- set rqdFld(mastersThesis) {author title school year}
- set optFld(mastersThesis) {address month note type}
-
- set rqdFld(misc) {}
- set optFld(misc) {author title howpublished year month note}
-
- set rqdFld(phdThesis) {author title school year}
- set optFld(phdThesis) {address month type note}
-
- set rqdFld(proceedings) {title year}
- set optFld(proceedings) {editor volume number series publisher organization address month note}
-
- set rqdFld(techReport) {author title institution year}
- set optFld(techReport) {type number address month note}
-
- set rqdFld(unpublished) {author title note}
- set optFld(unpublished) {year month}
-
- set entryNames [lsort [array names rqdFld]]
- set customEntries [lsort [array names myFld]]
-
- ###########################################################################
- # Define an array of flags indicating whether the data a given field
- # type should be quoted. The actual characters used to quote the field are
- # given by $bibOpenQuote and $bibCloseQuote, which are set by the routine
- # 'bibFieldDelims' according to the flag $fieldBraces.
- #
- # Note that the index names of the useBrace() array _define_ the valid
- # field types recognized by the program.
- #
- set useBrace(address) 1
- set useBrace(annote) 1
- set useBrace(author) 1
- set useBrace(booktitle) 1
- set useBrace(chapter) 0
- set useBrace(crossref) 1
- set useBrace(edition) 1
- set useBrace(editor) 1
- set useBrace(howpublished) 1
- set useBrace(institution) 1
- set useBrace(journal) 1
- set useBrace(key) 1
- set useBrace(language) 1
- set useBrace(month) 1
- set useBrace(note) 1
- set useBrace(number) 0
- set useBrace(organization) 1
- set useBrace(pages) 0
- set useBrace(publisher) 1
- set useBrace(school) 1
- set useBrace(series) 1
- set useBrace(title) 1
- set useBrace(type) 1
- set useBrace(volume) 0
- set useBrace(year) 0
-
- set fieldNames [lsort [array names useBrace]]
- ###########################################################################
- # Default values for newly created fields
- #
- set defFldVal(language) "german"
-
- set fieldDefs [lsort [array names defFldVal]]
-
- ###########################################################################
- # Search patterns for entries and cite-keys
- #
- # set bibTopPat {^[ ]*@[a-zA-Z]+[¥{¥(]([-A-Za-z0-9_:/¥.]+)}
- # match entry type
- set bibTopPat {^[ ]*@([a-zA-Z]+)[¥{¥(]}
- # match cite-key
- set bibTopPat1 {^[ ]*@[a-zA-Z]+[¥{¥(][ ]*([^=, ]+)}
- # match type and cite-key
- set bibTopPat2 {^[ ]*@([a-zA-Z]+)[¥{¥(][ ]*([^=, ]+)}
- # match first field (no cite-key)
- set bibTopPat3 {^[ ]*@([a-zA-Z]+)[¥{¥(]([ ]*[a-zA-Z]+[ ]*=[ ]*)}
-
-
- ###########################################################################
- # BibTeX-mode mode definition
- ###########################################################################
- newModeVar Bib suffixString { ¥¥¥¥} 0
- newModeVar Bib prefixString {% } 0
- newModeVar Bib fillColumn {65} 0
- newModeVar Bib wordWrap {0} 1
- newModeVar Bib autoMark {1} 1
-
- newModeVar Bib wordBreak {[a-zA-Z0-9]+} 0
- newModeVar Bib wordBreakPreface {[^a-zA-Z0-9]} 0
- newModeVar Bib funcExpr $bibTopPat 0
-
- newModeVar Bib overwriteBuffer {1} 1
- newModeVar Bib fieldBraces {1} 1
- newModeVar Bib entryBraces {1} 1
- newModeVar Bib segregateStrings {1} 1
- newModeVar Bib markStrings {0} 1
- newModeVar Bib alignEquals {0} 1
- ###
- # newModeVar Bib emacsBibMode {0} 1
- # newModeVar Bib addCiteKeys {0} 1
- # newModeVar Bib checkSyntax {0} 1
- newModeVar Bib zapEmptyFields {0} 1
- newModeVar Bib descendingYears {1} 1
- ###
- newModeVar Bib indentString { } 0
- newModeVar Bib stdAbbrevs {jan feb mar apr may jun jul aug sep oct nov dec} 0
- # newModeVar Bib convert8bitAscii2TeX {0} 1
-
- set bibtexKeyWords {address annote author booktitle
- chapter city crossref edition editor howpublished institution
- journal key language month note number organization
- publisher pages school series title type
- volume year}
- regModeKeywords -e {%} -m {@} -c red -k blue Bib $bibtexKeyWords
- unset bibtexKeyWords
-
- # # Use a shadow proc to keep settings for 8-bit character conversion
- # # consistent between TeX and Bib modes.
- # #
- # trace variable BibmodeVars(convert8bitAscii2TeX) w shadowBib8bitConvert
- # proc shadowBib8bitConvert {name1 name2 op} {
- # global BibmodeVars TeXmodeVars
- #
- # # Use TeX-mode routines to actually do the key bindings.
- # #
- # if {$BibmodeVars(convert8bitAscii2TeX)} then {
- # toggle8bitAscii "ascii" "Bib"
- # } else {
- # toggle8bitAscii "unascii" "Bib"
- # }
- #
- # # Only set TeX flag if necessary, to avoid unnecessary rebinding of keys
- # # (It takes enough time to be annoying)
- # #
- # if {$BibmodeVars(convert8bitAscii2TeX) != $TeXmodeVars(convert8bitAscii2TeX)} then {
- # set TeXmodeVars(convert8bitAscii2TeX) $BibmodeVars(convert8bitAscii2TeX)
- # }
- # }
- #
- # set BibmodeVars(convert8bitAscii2TeX) $TeXmodeVars(convert8bitAscii2TeX)
-
- ###########################################################################
- # BibTeX Menu Definition.
- ###########################################################################
- proc bibtexMenu {} {}
-
- set bibtexMenu "・136"
-
- proc bibtex {} {
- global bibtexSig
- set name [launchBackApplSigs {BIBt Vbib} bibtexSig]
- switchTo [file tail $name]
- }
-
- proc makeindex {} {
- launchForeAppl Midx
- }
-
- menu -n $bibtexMenu {
- "bibtex"
- "(-)"
- {menu -n Entries -p makeEntry {}
- }
- {menu -n Fields -p makeField {}
- }
- "(-)"
- "selectEntry/B<U<B"
- "nextEntry/N<U<B"
- "prevEntry/P<U<B"
- "formatEntry/L<U<B"
- "copyCiteKey/C<U<B"
- "(-)"
- "searchEntries/M<U<B"
- "searchFields/F<U<B"
- {menu -n sortBy... -p bibSortProc {
- "citeKey"
- "firstAuthor,Year"
- "lastAuthor,Year"
- "year,FirstAuthor"
- "year,LastAuthor"
- }
- }
- {menu -n sortMarks... -p markSortProc {
- "alphabetically"
- "byPosition"
- }
- }
- "(-)"
- "countEntries"
- "formatAllEntries"
- }
-
- menu -n Entries -p makeEntry [concat $entryNames {
- "(-)"
- "customEntry"
- } ]
-
- menu -n Fields -p makeField [concat $fieldNames {
- "(-)"
- "customField"
- "multipleFields"
- } ]
-
- ###########################################################################
- # Menu command procs
- ###########################################################################
-
- proc makeField {menu item} {
- global fieldNames
- bibFormatSetup
-
- if {$item == "multipleFields"} then {
- set flds [listpick -l -L {author year} -p "Pick desired fields:" $fieldNames]
- if {[llength flds]} {
- set lines {}
- foreach fld $flds {
- append lines [newField $fld]
- }
- } else {
- return
- }
- } else {
- set lines [newField $item]
- }
-
- set pos0 [nextLineStart [getPos]]
- goto $pos0
- insertText $lines
- goto $pos0
- nextTabStop
- }
-
- proc makeEntry {menu item} {
- bibFormatSetup
- newEntry $item
- }
-
- ###########################################################################
- # Return the bounds of the bibliographic entry surrounding the current
- # position.
- #
- proc getEntry {pos} {
-
- set pos1 [search -f 0 -r 1 -n -s {[ ]*@[a-zA-Z]*[¥{¥(]} $pos ]
- if {$pos1 == ""} then {
- set begPos [nextLineStart $pos]
- set endPos $begPos
- } else {
- set begPos [lineStart [lindex $pos1 0]]
- set pos0 [lindex $pos1 1]
- set openBrace [getText [expr $pos0-1] $pos0 ]
- if {[catch {matchIt $openBrace $pos0]} pos1]} {
- alertnote "There seems to be a badly delimited field in here. Are entry and field delimiters set correctly?"
- goto $begPos
- error "Can't find close brace"
- } else {
- set endPos [nextLineStart $pos1]
- }
- }
- return [list $begPos $endPos]
- }
-
- ###########################################################################
- # Advance to the next bibliographic entry.
- #
- proc nextEntry {} {
- global bibTopPat bibTopPat1 bibTopPat2
- # set topPat {[ ]*@([a-zA-Z]+)[¥{¥(]}
-
- set pos0 [lindex [getEntry [getPos]] 1]
- set nextPos [nextLineStart $pos0]
-
- while {![catch {search -f 1 -r 1 -s $bibTopPat $pos0} pos]} {
- regexp $bibTopPat [eval getText $pos] mtch type
- if {$type != "string"} {
- set nextPos [lindex $pos 0]
- break
- } else {
- set pos0 [nextLineStart [lindex $pos 1]]
- }
- }
- goto $nextPos
- }
-
- ###########################################################################
- # Go back to the previous bibliographic entry.
- #
- proc prevEntry {} {
- global bibTopPat bibTopPat1 bibTopPat2
- # set topPat {[ ]*@([a-zA-Z]+)[¥{¥(]}
-
- set pos0 [lindex [getEntry [getPos]] 0]
- if {$pos0 > 0} {
- set nextPos $pos0
- incr pos0 -1
- while {![catch {search -f 0 -r 1 -s $bibTopPat $pos0} pos]} {
- regexp $bibTopPat [eval getText $pos] mtch type
- if {$type != "string"} {
- set nextPos [lindex $pos 0]
- break
- } else {
- set pos0 [lineStart [lindex $pos 0]]
- if {$pos0 == 0} {break}
- incr pos0 -1
- }
- }
- goto $nextPos
- }
- }
-
- ###########################################################################
- # Select (highlight) the current bibliographic entry.
- #
- proc selectEntry {} {
- set pos [getEntry [getPos]]
- select [lindex $pos 0] [lindex $pos 1]
- }
-
- ###########################################################################
- # Put the cite-key of the current entry on the clipboard.
- #
- proc copyCiteKey {} {
- global bibTopPat2
- set limits [getEntry [getPos]]
- set top [lindex $limits 0]
- set bottom [lindex $limits 1]
- if {[regexp -indices $bibTopPat2 [getText $top $bottom] allofit type citekey]} {
- select [expr $top+[lindex $citekey 0]] [expr $top+[lindex $citekey 1]+1]
- copy
- message "Copied ¥"[getSelect]¥""
- }
- }
-
- ###########################################################################
- # Create a new bibliographic entry with its required fields.
- #
- proc newEntry {entryName} {
- global entryNames customEntries fieldNames rqdFld optFld myFld defFldVal
- global bibOpenEntry bibCloseEntry BibmodeVars
- goto [lindex [getEntry [getPos]] 1]
- if {$entryName == "customEntry"} {
- set lines "@・$bibOpenEntry・,¥r"
- set theFields [listpick -l -L {author} -p "Pick desired fields:" $fieldNames]
- } else {
- set lines "@${entryName}$bibOpenEntry・,¥r"
- if {[lsearch -exact $customEntries $entryName] >= 0 && [llength $myFld($entryName)]} {
- set theFields $myFld($entryName)
- } elseif {[lsearch -exact $entryNames $entryName] >= 0} {
- set theFields $rqdFld($entryName)
- } else {
- set theFields {}
- }
- }
- set nmlen 0
- foreach field $theFields {
- set len [string length $field]
- if {$len > $nmlen} {set nmlen $len}
- }
- set theTop [lineStart [getPos]]
- foreach field $theFields {
- catch {append lines [newField $field $nmlen]}
- }
- append lines "$bibCloseEntry¥r"
- insertText $lines
- goto $theTop
- nextTabStop
- }
-
- ###########################################################################
- # Create a new field within the current bibliographic entry
- #
- proc newField {fieldName {nmlen 0}} {
- global fieldNames useBrace bibOpenQuote bibCloseQuote bibIndent
- global fieldDefs defFldVal
- set spc " "
- if {[lsearch -exact $fieldNames $fieldName] >= 0} {
- set needBraces $useBrace($fieldName)
- } else {
- set needBraces 1
- }
-
- if {[lsearch -exact $fieldDefs $fieldName] >= 0} {
- set val $defFldVal($fieldName)
- } else {
- set val "・"
- }
-
- if {$nmlen} {
- set pad [string range $spc 1 [expr $nmlen - [string length $fieldName]]]
- } else {
- set pad ""
- }
- if {$needBraces || $fieldName == "customField"} {
- set result "$bibIndent$fieldName =$pad ${bibOpenQuote}${val}${bibCloseQuote},¥r"
- } else {
- set result "$bibIndent$fieldName =$pad $val,¥r"
- }
- return $result
- }
-
- proc bibFormatSetup {} {
- global bibOpenQuote bibCloseQuote bibIndent BibmodeVars
- global bibOpenEntry bibCloseEntry bibAbbrevs
- bibFieldDelims
- bibEntryDelims
- set bibIndent $BibmodeVars(indentString)
- regsub {¥¥t} $bibIndent { } bibIndent
- set bibAbbrevs [listStrings]
- foreach abbrev $BibmodeVars(stdAbbrevs) {
- lappend bibAbbrevs [string tolower $abbrev]
- }
- }
-
- ###########################################################################
- # Find all entries that match a given regular expression and copy them to
- # a new buffer.
- #
- proc searchEntries {} {
- if [catch {prompt "Regular expression:" ""} reg] return
- if {![string length $reg]} return
- set reg ^.*$reg.*$
-
- set matches [findEntries $reg]
- if {[llength $matches] >0} {
- writeEntries $matches 0
- } else {
- message "No matching entries were found"
- }
- }
-
- ###########################################################################
- # Find all entries in which the indicated field matches a given regular
- # expression and copy them to a new buffer.
- #
- proc searchFields {} {
- global fieldNames
- if {[catch {eval prompt {{Field name:}} "author" {Fields} $fieldNames} fld]} return
- if {![string length $fld]} return
-
- if {[catch {prompt "Regular expression:" ""} reg]} return
- if {![string length $reg]} return
-
- set matches [findEntries $reg]
- if {[llength $matches] == 0} {
- return "No matching entries were found"
- }
-
- set vals {}
- foreach hit $matches {
- set pos [lindex $hit 1]
- set top [lindex $hit 2]
- set bottom [lindex $hit 3]
- while {[set failure [expr {[getFldName $pos $top] != $fld}]] &&
- ![catch {search -f 1 -r 1 -i 1 -m 0 -l $bottom -s -- $reg $pos} mtch]} {
- set pos [lindex $mtch 1]
- }
- if {!$failure} { lappend vals [list $top $bottom] }
- }
-
- if {[llength $vals] >0} {
- writeEntries $vals 0
- } else {
- message "No matching entries were found"
- }
-
- }
-
- ###########################################################################
- # Sort all of the entries based on one of various criteria.
- #
- proc bibSortProc {menu item} {
- if {$item == "citeKey"} {
- sortByCiteKey
- } elseif {$item == "firstAuthor,Year"} {
- sortByAuthors 0 0
- } elseif {$item == "lastAuthor,Year"} {
- sortByAuthors 1 0
- } elseif {$item == "year,FirstAuthor"} {
- sortByAuthors 0 1
- } elseif {$item == "year,LastAuthor"} {
- sortByAuthors 1 1
- }
- }
-
- ###########################################################################
- # Sort the file marks. (These operations are also available under the
- # "Search:NamedMarks" menu)
- #
- proc markSortProc {menu item} {
- if {$item == "alphabetically"} {
- sortMarksFile
- } elseif {$item == "byPosition"} {
- orderMarks
- }
- }
-
- ###########################################################################
- # Sort all of the entries in the file alphabetically by author.
- #
- proc sortByAuthors {{lastAuthorFirst 0} {yearFirst 0}} {
- global bibTopPat bibTopPat1 bibTopPat2 BibmodeVars
- set bibSegStr $BibmodeVars(segregateStrings)
-
- set matches [findEntries $bibTopPat]
- set crossrefs [listCrossrefs]
- set strings [listStrings]
-
- set vals {}
- set others {}
- set refs {}
- set strs {}
-
- set beg [maxPos]
- set end 0
-
- foreach hit $matches {
- set pos [lindex $hit 1]
- set top [lindex $hit 2]
- set bottom [lindex $hit 3]
- set entry [getText $top $bottom]
- regsub -all "¥[¥n¥r¥]+" $entry { } entry
- regsub -all "¥[ ¥]¥[ ¥]+" $entry { } entry
- regsub {[, ]*[¥)¥}][ ]*$} $entry { } entry
- if {[regexp $bibTopPat1 $entry allofit citeKey]} {
- set citeKey [string tolower $citeKey]
- set keyExists 1
- } else {
- set citekey ""
- set keyExists 0
- }
-
- if {$keyExists && [lsearch -exact $crossrefs $citeKey] >= 0} {
- lappend refs [list $pos $top $bottom]
- } elseif {$bibSegStr && $keyExists && [lsearch -exact $strings $citeKey] >= 0} {
- lappend strs [list $citeKey $top $bottom]
- } else {
- if {![catch {getFldValue $entry author} fldval]} {
- if {[catch {getFldValue $entry year} year]} { set year 9999 }
- lappend vals [list [authSortKey $fldval $lastAuthorFirst $year $yearFirst] $top $bottom]
- } else {
- lappend others [list $pos $top $bottom]
- }
- }
- if {$top < $beg} {set beg $top}
- if {$bottom > $end} {set end $bottom}
- }
-
- if {$bibSegStr} {
- set result [concat $strs $others [lsort $vals] $refs]
- } else {
- set result [concat $others [lsort $vals] $refs]
- }
-
- if {[llength $result] >0} {
- writeEntries $result 1 $beg $end
- } else {
- message "No results of author sort !!??"
- }
- }
-
- ###########################################################################
- # Return a list of the cite-keys of all cross-referenced entries.
- #
- proc listStrings {} {
- global bibTopPat bibTopPat1 bibTopPat2
- set matches [findEntries {^[ ]*@string *[¥{¥(]} 0]
-
- message "scanning for @stringsノ"
- foreach hit $matches {
- set top [lindex $hit 2]
- set bottom [lindex $hit 3]
- set entry [getText $top $bottom]
- regsub -all "¥[¥n¥r¥]+" $entry { } entry
- regsub -all "¥[ ¥]¥[ ¥]+" $entry { } entry
- regsub {[, ]*[¥)¥}][ ]*$} $entry { } entry
- regexp $bibTopPat1 $entry allofit citekey
- set citekey [string tolower $citekey]
- if {[catch {incr strings($citekey)} num]} {
- set strings($citekey) 1
- }
- }
- if {[catch {lsort [array names strings]} res]} {
- set res {}
- }
- message ""
- return $res
- }
-
- ###########################################################################
- # Return a list of the cite-keys of all cross-referenced entries.
- #
- proc listCrossrefs {} {
- set matches [findEntries {crossref}]
- catch {unset crossrefs}
-
- message "scanning for crossrefsノ"
- foreach hit $matches {
- set top [lindex $hit 2]
- set bottom [lindex $hit 3]
- set entry [getText $top $bottom]
- regsub -all "¥[¥n¥r¥]+" $entry { } entry
- regsub -all "¥[ ¥]¥[ ¥]+" $entry { } entry
- regsub {[, ]*[¥)¥}][ ]*$} $entry { } entry
- if {![catch {getFldValue $entry crossref} fldval]} {
- set fldval [string tolower $fldval]
- if {[catch {incr crossref($fldval)} num]} {
- set crossrefs($fldval) 1
- }
- }
- }
- if {[catch {lsort [array names crossrefs]} res]} {
- set res {}
- }
- message ""
- return $res
- }
-
- ###########################################################################
- # Create a sort key from an author list. When sorting entries by author,
- # performing the sort using keys should be faster than reparsing the author
- # lists for every comparison (the old method :-( ).
- #
- proc authSortKey {authList lastAuthorFirst {year {}} {yearFirst 0}} {
- global BibmodeVars
- set pat1 {¥¥.¥{([A-Za-z])¥}}
- set pat2 {¥{([^¥{¥}]+) ([^¥{¥}]+)¥}}
-
- # Remove enclosing braces, quotes, or whitespace
- set auths %[string trim $authList {{}" }]&
- # Remove TeX codes for accented characters
- regsub -all $pat1 $auths {¥1} auths
- # Concatenate strings enclosed in braces
- while {[regsub -all $pat2 $auths {{¥1¥2}} auths]} {}
- # Remove braces (curly and square)
- regsub -all {[][¥{¥}]} $auths {} auths
- # regsub -all {,} $auths { ,} auths
- # Replace 'and's with begin-name/end-name delimiters
- regsub -all {[ ]and[ ]} $auths { ¥&% } auths
- # Put last name first in name fields without commas
- regsub -all {%([^¥&,]+) ([^¥&, ]+) *¥&} $auths {%¥2,¥1¥&} auths
- # Remove begin-name delimiters
- regsub -all {%} $auths {} auths
- # Remove whitespace surrounding name separators
- regsub -all {[ ]*¥&[ ]*} $auths {¥&} auths
- # Replace whitespace separating words with shrieks
- regsub -all {[ ,]+} $auths {!} auths
- # If desired, move last author to head of sort key
- if {$lastAuthorFirst} {
- regsub {(.*)&([^&]+)&?$} $auths {¥2¥&¥1} auths
- }
- # If provided, sort by year (descending order) as well
- regsub {^[^0-9]*([0-9]*).*$} $year {¥1} year
- if {$year != {}} {
- if {$BibmodeVars(descendingYears)} { catch {set year [expr 9999-$year]} }
- if {$yearFirst} {
- set auths "$year&$auths"
- } else {
- regsub {^([^&]+)(&?)} $auths "¥¥1¥¥&${year}¥¥2" auths
- }
- }
-
- return $auths
- }
-
- ###########################################################################
- # Sort all of the entries in the file alphabetically by their cite-keys.
- #
- proc sortByCiteKey {} {
- global bibTopPat bibTopPat1 bibTopPat2 BibmodeVars
- set bibSegStr $BibmodeVars(segregateStrings)
-
- set matches [findEntries $bibTopPat]
- set crossrefs [listCrossrefs]
- set strings [listStrings]
-
- set begEntries [maxPos]
- set endEntries 0
-
- set strs {}
- set vals {}
- set refs {}
-
- foreach hit $matches {
- set beg [lindex $hit 0]
- set end [lindex $hit 1]
- set top [lindex $hit 2]
- set bottom [lindex $hit 3]
- if {[regexp $bibTopPat1 [getText $top $bottom] allofit citekey]} {
- set citekey [string tolower $citekey]
- set keyExists 1
- } else {
- set citekey "000000$beg"
- set keyExists 0
- }
-
- if {$keyExists && [lsearch -exact $crossrefs $citekey] >= 0} {
- lappend refs [list $top $top $bottom]
- } elseif {$keyExists && $bibSegStr && [lsearch -exact $strings $citekey] >= 0} {
- lappend strs [list $citekey $top $bottom]
- } else {
- lappend vals [list $citekey $top $bottom]
- }
-
- if {$top < $begEntries} {set begEntries $top}
- if {$bottom > $endEntries} {set endEntries $bottom}
- }
-
- if {$bibSegStr} {
- set result [concat $strs [lsort $vals] $refs]
- } else {
- set result [concat [lsort $vals] $refs]
- }
-
- if {[llength $result] >0} {
- writeEntries $result 1 $begEntries $endEntries
- } else {
- message "No results of cite-key sort !!??"
- }
- }
-
- ###########################################################################
- # Search for all entries matching a given regular expression. The results
- # are returned in a list, each element of which is a list of four integers:
- # the beginning and end of the matching entry and the beginning and end of
- # the matching string. Adapted from "matchingLines" in "misc.tcl".
- #
- proc findEntries {reg {casesen 1}} {
- if {![string length $reg]} return
-
- set pos 0
- set result {}
- while {![catch {search -f 1 -r 1 -m 0 -i $casesen -s $reg $pos} mtch]} {
- set entry [getEntry [lindex $mtch 0]]
- lappend result [concat $mtch $entry]
- set pos [lindex $entry 1]
- }
- return $result
- }
-
- ###########################################################################
- # Return a list containing the data for the current entry, indexed by
- # the parameter names, e.g., "author", "year", etc. Index names for the
- # entry type and cite-key are "type" and "citekey".
- #
- proc getFields {pos} {
- global bibTopPat bibTopPat1 bibTopPat2 bibTopPat3
- set fldPat {[ ]*([a-zA-Z]+)[ ]*=[ ]*}
-
- set limits [getEntry $pos]
- set top [lindex $limits 0]
- set bottom [lindex $limits 1]
-
- set entry [getText $top $bottom]
- regsub -all "¥[¥n¥r¥]+" $entry { } entry
- regsub -all "¥[ ¥]¥[ ¥]+" $entry { } entry
- #
- regsub {[, ]*[¥)¥}][ ]*$} $entry { } entry
-
- if {[regexp -indices $bibTopPat2 $entry mtch theType theKey ]} {
- set key [string range $entry [lindex $theKey 0] [lindex $theKey 1]]
- set theRest [expr 1 + [lindex $mtch 1]]
- } elseif {[regexp -indices $bibTopPat3 $entry mtch theType aField]} {
- set key {}
- set theRest [lindex $aField 0]
- } else {
- error "Invalid entry"
- }
- lappend names type
- set type [string tolower [string range $entry [lindex $theType 0] [lindex $theType 1]]]
- lappend data [list $type]
-
- lappend names citekey
- lappend data $key
-
- set entry ",[string range $entry $theRest end]"
- set fldPat {,[ ]*([^ =,]+)[ ]*=[ ]*}
- set name {}
- while {[regexp -indices $fldPat $entry mtch sub1]} {
- set nextName [string range $entry [lindex $sub1 0] [lindex $sub1 1]]
- lappend names [string tolower $nextName]
- if {$name != ""} {
- set prevData [string range $entry 0 [expr [lindex $mtch 0]-1]]
- lappend data [breakIntoLines [bibFieldData $prevData]]
- }
- set name $nextName
- set entry [string range $entry [expr [lindex $mtch 1]+1] end]
- }
-
- lappend data [breakIntoLines [bibFieldData $entry]]
-
- return [list $names $data]
- }
-
- proc bibFieldData {text} {
- set text [string trim $text { ,#}]
- set text1 [string trim $text {¥{¥}¥" }]
-
- if {[string match {*[¥{¥}¥"]*} $text1]} {
- set words [parseWords $text]
- if {[llength $words]==1} {
- regsub {^[¥{¥"¥']} $text {} text
- regsub {[¥}¥"¥']$} $text {} text
- }
- } else {
- set text $text1
- }
- return $text
- }
-
-
- ###########################################################################
- # Extract the data from the indicated field of an entry, which is passed
- # as a single string. This version tries to be completely general,
- # allowing nested braces within data fields and ignoring escaped
- # delimiters. (derived from proc getField).
- #
- proc getFldValue {entry fldname} {
- set fldPat "¥[ ¥]*${fldname}¥[ ¥]*=¥[ ¥]*"
- set fldPat2 {,[ ]*([^ =,]+)[ ]*=[ ]*}
- set slash "¥¥"
- set qslash "¥¥¥¥"
-
- set ok [regexp -indices -nocase $fldPat $entry mtch]
- if {$ok} {
- set pos [expr [lindex $mtch 1] + 1]
- set entry [string range $entry $pos end]
-
- if {[regexp -indices $fldPat2 $entry mtch sub1]} {
- set entry [string range $entry 0 [expr [lindex $mtch 0]-1]]
- }
- set fld [bibFieldData $entry]
-
- return $fld
-
- } else {
- error "field not found"
- }
- }
-
- ###########################################################################
- # Parse the entry around position "pos" and rewrite it to the original
- # buffer in a canonical format
- #
- proc formatEntry {} {
- global useBrace bibOpenQuote bibCloseQuote
- global bibOpenEntry bibCloseEntry bibIndent
- set spc " "
-
- bibFormatSetup
-
- set pos [getPos]
- set limits [getEntry $pos]
- set top [lindex $limits 0]
- set bottom [lindex $limits 1]
-
- if {![catch {bibFormatEntry $pos} result]} {
- set oldEntry [getText $top $bottom]
- if {$result != $oldEntry} {
- deleteText $top $bottom
- insertText $result
- }
- goto $top
- nextEntry
- } else {
- message "Couldn't format this entry for some reason"
- }
- }
-
- ###########################################################################
- # Parse the entry around position "pos" and rewrite it to the original
- # buffer in a canonical format
- #
- proc formatAllEntries {} {
- global useBrace bibOpenQuote bibCloseQuote
- global bibOpenEntry bibCloseEntry bibIndent
- set spc " "
-
- bibFormatSetup
-
- # This little dance handles the case that the first
- # entry starts on the first line
- #
- set hit [getEntry [getPos]]
- if {[lindex $hit 0] == [lindex $hit 1]} {
- nextEntry
- set hit [getEntry [getPos]]
- }
-
- while {[getPos] < [lindex $hit 1]} {
- set top [lindex $hit 0]
- set bottom [lindex $hit 1]
-
- if {![catch {bibFormatEntry $top} result]} {
- set oldEntry [getText $top $bottom]
- if {$result != $oldEntry} {
- deleteText $top $bottom
- insertText $result
- }
- }
- goto $top
- nextEntry
- set hit [getEntry [getPos]]
- }
- }
-
- ###########################################################################
- # Parse the entry around position "pos" and rewrite it in a canonical format.
- # The formatted entry is returned.
- #
- proc bibFormatEntry {pos} {
- global useBrace bibOpenQuote bibCloseQuote
- global bibOpenEntry bibCloseEntry bibIndent
- global rqdFld optFld BibmodeVars bibAbbrevs
- set spc " "
- #
- # note: calling proc must call "bibFormatSetup" before calling "bibFormatEntry"
- #
- set limits [getEntry $pos]
- set top [lindex $limits 0]
- set bottom [lindex $limits 1]
-
- if {[catch {getFields $pos} flds]} {
- error "bibFormatEntry: Getflds couldn't find any"
- }
-
- set names [lindex $flds 0]
- set vals [lindex $flds 1]
- set nfld [llength $names]
-
- set type [string tolower [lindex $vals 0]]
- set citekey [lindex $vals 1]
- # message "$citekey"
- # Don't process @string entries
- if {$type == "string"} {
- set lines [getText $top $bottom]
- return $lines
- }
- # Find length of longest field name
- set nmlen 0
- foreach nm $names {
- set len [string length $nm]
- if {$len > $nmlen} { set nmlen $len }
- if {![info exists useBrace($nm)]} { set useBrace($nm) 0 }
- }
-
- # Format first line
- set lines "@${type}${bibOpenEntry}${citekey},¥r"
-
- # Format each field on a separate line
- for {set ifld 2} {$ifld < $nfld} {incr ifld} {
- set nm [lindex $names $ifld]
- set vl [lindex $vals $ifld]
- if {$vl != "" || ! $BibmodeVars(zapEmptyFields) ||
- [lsearch $rqdFld($type) $nm] >= 0} {
- set pad [expr $nmlen - [string length $nm]]
-
- if {$BibmodeVars(alignEquals)} {
- set pref "${bibIndent}$nm[string range $spc 1 $pad] ="
- } else {
- set pref "${bibIndent}$nm =[string range $spc 1 $pad]"
- }
- set ind [string range $spc 1 [string length $pref]]
-
- # Delimit field, if appropriate
- set noBrace [expr ($useBrace($nm) == 0 && [isNum $vl]) || [hasCat $vl]]
- if {$noBrace == 0 && [string first " " $vl] < 0} {
- set noBrace [expr [lsearch $bibAbbrevs [string tolower $vl]] >= 0]
- }
- if {$noBrace != 0} {
- set vl "$vl,"
- } else {
- set vl "${bibOpenQuote}${vl}${bibCloseQuote},"
- }
-
- set pieces [split $vl "¥r"]
- append lines "$pref [lindex $pieces 0]¥r"
- foreach piece [lrange $pieces 1 end] {
- append lines "$ind $piece¥r"
- }
- }
- }
- append lines "$bibCloseEntry¥r"
- return $lines
- }
-
- ###########################################################################
- # Get the name of the field that starts before the given position,
- # $pos. The positions $top and $bottom restrict the range of the
- # search for the beginning and end of the field; typically, $top and
- # $bottom will be the limits of a given entry.
- #
- proc getFldName {pos top} {
- set fldPat {[, ]+([^ =,¥{¥}¥"¥']+)[ ]*=[ ]*}
- if {![catch {search -f 0 -r 1 -m 0 -i 1 -s -limit $top "$fldPat" $pos} mtch]} {
- set theText [eval getText $mtch]
- regexp -nocase $fldPat $theText allofit fldnam
- return $fldnam
- } else {
- return {citekey}
- }
- }
-
- ###########################################################################
- # Set the quote characters for quoted fields based on the value of the
- # flag $bibUseBrace
- #
- proc bibFieldDelims {} {
- global BibmodeVars bibOpenQuote bibCloseQuote
- if {$BibmodeVars(fieldBraces)} then {
- set bibOpenQuote "{"
- set bibCloseQuote "}"
- } else {
- set bibOpenQuote {"}
- set bibCloseQuote {"}
- }
- }
-
- proc bibEntryDelims {} {
- global BibmodeVars bibOpenEntry bibCloseEntry
- if {$BibmodeVars(entryBraces)} then {
- set bibOpenEntry "{"
- set bibCloseEntry "}"
- } else {
- set bibOpenEntry "("
- set bibCloseEntry ")"
- }
- }
-
- proc isBibFile {} {
- set fileName [car [winNames -f]]
- set ext [file extension $fileName]
- return [string match ".bib" [string tolower $ext]]
- }
-
- proc hasNumVal {str} {
- expr ! [catch {expr $str}]
- }
- proc isNum {str} {
- regexp {^[ ]*[0-9]+[ ]*$} $str mtch
- }
- proc hasCat {str} {
- regexp {¥#} $str mtch
- }
-
- ###########################################################################
- # Take a list of lists that point to selected entries and copy these into
- # a new window. The beginning and ending positions for each entry must
- # be the last two items in each sublist. The rest of the sublists are
- # ignored. It is assumed that each sublist has the same number of items.
- #
- proc writeEntries {entryPos nondestructive {beg {0}} {end {-1}}} {
- global BibmodeVars
- if {$end < 0} {set end [maxPos]}
- set llen [expr [llength [lindex $entryPos 0]] - 1]
- set llen1 [expr $llen-1]
- foreach entry $entryPos {
- set limits [lrange $entry $llen1 $llen]
- append lines [eval getText $limits]
- }
- set overwriteOK [expr $nondestructive || ! [isBibFile]]
- if {$BibmodeVars(overwriteBuffer) && $overwriteOK} {
- deleteText $beg $end
- insertText $lines
- goto $beg
- } else {
- set begLines [getText 0 [lineStart $beg]]
- set endLines [getText [nextLineStart $end] [maxPos]]
- new -n {*BibTeX Sort/Search*}
- newMode Bib
- insertText $begLines
- insertText $lines
- insertText $endLines
- goto $beg
- setWinInfo dirty 0
- catch shrinkWindow
- }
- }
-
- ###########################################################################
- # Set a named mark for each entry, using the cite-key name
- #
- proc BibMarkFile {} {
- global BibmodeVars
- global bibTopPat bibTopPat1 bibTopPat2
- set pos 0
- while {![catch {search -f 1 -r 1 -m 0 -i 0 -s $bibTopPat1 $pos} res]} {
- set start [lindex $res 0]
- set end [nextLineStart $start]
- set text [getText $start $end]
- set lab ""
- if {[regexp $bibTopPat2 $text mtch type citekey]} {
- if {[string tolower $type] != "string" || $BibmodeVars(markStrings)} {
- setNamedMark $citekey [lineStart [expr $start - 1]] $start $start
- }
- }
- set pos $end
- }
- }
-
- ###########################################################################
- # Report the number of entries of each type
- #
- proc countEntries {} {
- global entryNames
- global bibTopPat bibTopPat1 bibTopPat2
-
- set pos 0
- set count 0
- catch {unset type}
-
- while {![catch {search -f 1 -r 1 -m 0 -i 0 -s $bibTopPat $pos} res]} {
- incr count
- set start [lindex $res 0]
- set end [nextLineStart $start]
- set text [getText $start $end]
- set lab ""
- if {[regexp $bibTopPat $text mtch entryType]} {
- set entryType [string tolower $entryType]
- if {[catch {incr type($entryType)} num]} {
- set type($entryType) 1
- }
- }
- set pos $end
- }
- new -n {*BibTeX Statistics*}
- newMode Bib
- foreach name [lsort [array names type]] {
- if {$type($name) > 0} {
- append lines [format "%4.0d %s¥n" $type($name) $name]
- }
- }
- append lines "---- -----------------¥n"
- append lines [format "%4.0d %s¥n" $count "Total entries"]
- insertText $lines
- goto 0
- setWinInfo dirty 0
- catch {shrinkWindow 1}
- }
- #--------------------------------------------------------------------------
- # command-double-clicking:
- #--------------------------------------------------------------------------
-
- ###########################################################################
- # In Bib mode, Cmd-double-clicks resolve abbrevs and cross-refs
- #
- proc BibDblClick {from to} {
- global bibTopPat bibTopPat1 bibTopPat2
-
- set limits [getEntry $from]
- set top [lindex $limits 0]
- set bottom [lindex $limits 1]
-
- # Extend selection to largest string that could be an entry reference
- set text [string trim [eval getText [BibExtendClick $from $to $top $bottom]]]
-
- # Get the citekey of current entry, so we can avoid jumping to it
- set citekey {}
- regexp $bibTopPat2 [getText $top $bottom] mtch type citekey ]
- set fldName [getFldName $from $top]
-
- if {[string length $text] == 0 || $text == $citekey || $fldName == $text ||
- ($fldName == "citekey" && [string tolower $type] != "string")} {
- message "Command-double-click on abbreviations and crossref arguments"
- return
- }
-
- # Jump to the mark for the specified citation, if a mark exists...
- # ...otherwise, do an ordinary search for the cite key
- pushMark
- set searchPat "$bibTopPat¥[ ¥]*[quoteExpr $text]¥[ ,¥}¥)¥]"
- if {![catch {search -f 1 -r 1 -i 1 -m 0 $searchPat 0} mtch]} {
- goto [lindex $mtch 0]
- } else {
- popMark
- select $from $to
- if {$fldName == "crossref"} {
- message "Cross-reference ¥"$text¥" not found"
- } else {
- message "Command-double-click on abbreviations and crossref arguments"
- }
- return
- }
- message "Use Ctl-. to return to original position"
- return
- }
-
- # Extend the selection around the initial selection {$from,$to}
- # Extension is restricted to the range {$top,$bottom} (the current entry)
- proc BibExtendClick {from to top bottom} {
- if {$to == 0} { set to $from }
- set result [list $from $to]
- if {![catch {search -f 0 -r 1 -s -m 0 -l $top "¥[,¥{¥]¥"¥'=" $from} mtch0]} {
- if {![catch {search -f 1 -r 1 -s -m 0 -l $bottom "¥[,¥}¥]¥"¥'=" $to} mtch1]} {
- set from [lindex $mtch0 1]
- set to [lindex $mtch1 0]
- # Check for illegal chars embedded in the selection
- if {[regexp "¥[¥{¥}¥]=" [getText $from $to]] == 0} {
- set result [list $from $to]
- }
- }
- }
- return $result
- }
-
-
- ###########################################################################
-
- proc dummyBibTeX {} {
- global BibmodeVars TeXmodeVars
- # if {$BibmodeVars(convert8bitAscii2TeX) != $TeXmodeVars(convert8bitAscii2TeX)} {
- # set BibmodeVars(convert8bitAscii2TeX) $TeXmodeVars(convert8bitAscii2TeX)
- # }
- }
-
- #
-
- #===============================================================================
- proc pcite {} {
- set words [getline "Citation keys" ""]
- if {![llength $words]} {error "No keys"}
-
- set pattern {@}
- foreach w $words {
- append pattern "(¥[^@¥]+$w)"
- }
-
- foreach entry [findEntries $pattern] {
- set res [getFields [car $entry]]
- set title [lindex [cadr $res] [lsearch [car $res] "title"]]
- set citekey [lindex [cadr $res] [lsearch [car $res] "citekey"]]
- set matches($title) $citekey
- set where($title) [car $entry]
- }
- if {![info exists matches]} {alertnote "No citations"; return}
- set title [listpick -p "Citation?" [lsort [array names matches]]]
- putScrap $matches($title)
- alertnote $matches($title)
- goto $where($title)
- }
-